﻿using eLoan.Models;
using iTextSharp.text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using System.Web.UI.HtmlControls;

namespace eLoan.Controllers
{
    public class LoanScheduleController : BaseController
    {
        public ActionResult Edit(int? id, string documentNo)
        {
            try
            {
                var db = new DatabaseContext();
                var chargeTypes = db.ChargeTypes;

                ViewBag.DocumentNo = documentNo;
                ViewData[AdministrationHelper.strChargeTypes] = AdministrationHelper.GetChargeTypeDropDownList(chargeTypes);

                var loanPaymentSchedules = db.LoanPaymentSchedules;
                var loanPaymentSchedule = loanPaymentSchedules.Where(w => w.ID == id).SingleOrDefault<LoanPaymentSchedule>();

                return View("Edit", loanPaymentSchedule);
            }
            catch (Exception ex)
            {
                return View("Error", ex);
            }
        }

        [HttpPost]
        public ActionResult Save(LoanPaymentSchedule loanPaymentSchedule, string afterSave)
        {
            try
            {
                string documentNo = "0";
                var arr = afterSave.Split(',');
                documentNo = arr[1];

                if (ModelState.IsValid)
                {
                    using (var db = new DatabaseContext())
                    {
                        
                        if (arr[0] == AdministrationHelper.DBCommand.Edit.ToString())
                        {
                            loanPaymentSchedule = CheckDueAmount(loanPaymentSchedule);
                            db.Entry(loanPaymentSchedule).State = System.Data.EntityState.Modified;
                            db.SaveChanges();
                        }
                        else
                        {
                            // cancelled
                        }
                    }
                }

                return RedirectToAction("LoanDetails", "Loan", new { documentNo = documentNo });
            }
            catch (Exception ex)
            {
                return View("Error", ex);
            }
        }

        private LoanPayment CheckVoidAndPaymentSchedule(LoanPayment loanPayment)
        {
            loanPayment.VoidBy = loanPayment.IsVoid ? User.Identity.Name : string.Empty;
            loanPayment.VoidDate = loanPayment.IsVoid ? DateTime.Now : (DateTime?)null;
            loanPayment.Remarks = loanPayment.IsVoid ? loanPayment.Remarks : string.Empty;

            return loanPayment;
        }

        public ActionResult CreatePaymentSchedule(string documentNo)
        {
            try 
            {
                var db = new DatabaseContext();
                var loan = db.Loans.Where(w => w.DocumentNo == documentNo).SingleOrDefault<Loan>();

                DeleteLoanPaymentSchedule(db, documentNo);
                GenerateLoanSchedule(db, loan);

                return RedirectToAction("LoanDetails", "Loan", new { documentNo = documentNo });

            }
            catch (Exception ex)
            {
                return View("Error", ex);
            }
        }

        [HttpPost]
        public ActionResult CreateNewPaymentSchedule(GenerateNewScheduleModel model, string command)
        {
            try
            {
                var db = new DatabaseContext();
                var loan = db.Loans.Where(w => w.DocumentNo == model.DocumentNo).SingleOrDefault<Loan>();

                switch (command)
                {
                    case "CreateSchedule":
                        {
                            loan.PaymentStartDate = model.NewPaymentStartDate;

                            if (model.ProceedTypeID == (int)AdministrationHelper.ProceedType.SemiMonthly)
                            {
                                loan.PaymentSecondDate = model.PaymentSecondDate > 0 ? model.PaymentSecondDate : model.NewPaymentStartDate.GetValueOrDefault().AddDays(15).Day;
                            }

                            db.Entry(loan).State = System.Data.EntityState.Modified;

                            DeleteLoanPaymentSchedule(db, loan.DocumentNo);
                            GenerateLoanSchedule(db, loan);
                            break;
                        }
                    case "PrintSchedule":
                        {
                            PrintSchedule(db, loan.DocumentNo);
                            break;
                        }
                    case "Save":
                        {
                            SaveSchedule(db, model.Schedule);
                            break;
                        }
                    default:
                        {
                            // cancelled
                            break;
                        }
                }

                return RedirectToAction("GenerateSchedule", "Funding", new { documentNo = model.DocumentNo, source = model.Source });
            }
            catch (Exception ex)
            {
                return View("Error", ex);
            }
        }

        private List<LoanPaymentSchedule> GetScheduleDates(string documentNo, DateTime startDate, DateTime endDate, int proceedType)
        {
            var dueDate = startDate;
            var schedule = new List<LoanPaymentSchedule>();

            do
            {
                var paymentSchedule = new LoanPaymentSchedule();

                paymentSchedule.DocumentNo = documentNo;
                paymentSchedule.DueDate = dueDate;
                paymentSchedule.ScheduledAmount = 0;
                paymentSchedule.OtherCharges = 0;
                paymentSchedule.DueAmount = 0;

                if (dueDate.DayOfWeek != DayOfWeek.Sunday)
                    schedule.Add(paymentSchedule);

                if (dueDate == endDate)
                    break;

                dueDate = GetNextDueDate(dueDate, proceedType);

                if (dueDate > endDate)
                    dueDate = endDate;
            }
            while (dueDate <= endDate);

            return schedule;
        }

        private List<LoanPaymentSchedule> GetScheduleDates(Loan loan)
        {
            var dueDate = loan.PaymentStartDate.GetValueOrDefault();
            var schedule = new List<LoanPaymentSchedule>();

            for (var i = 1; i <= loan.Term; i++)
            {
                var paymentSchedule = new LoanPaymentSchedule();

                paymentSchedule.DocumentNo = loan.DocumentNo;
                paymentSchedule.DueDate = dueDate;
                paymentSchedule.ScheduledAmount = 0;
                paymentSchedule.OtherCharges = 0;
                paymentSchedule.DueAmount = 0;

                if (dueDate.DayOfWeek == DayOfWeek.Sunday)
                {
                    if (loan.ProceedTypeID == (int)AdministrationHelper.ProceedType.Daily)
                    {
                        dueDate = GetNextDueDate(dueDate, loan.ProceedTypeID);
                        continue;
                    }
                    else
                    {
                        paymentSchedule.DueDate = dueDate.AddDays(1);
                    }
                }

                schedule.Add(paymentSchedule);

                if (loan.ProceedTypeID == (int)AdministrationHelper.ProceedType.SemiMonthly)
                {
                    var firstDate = loan.PaymentStartDate.GetValueOrDefault().Day;
                    var secondDate = loan.PaymentSecondDate;

                    repeat:

                    try
                    {
                        if (firstDate < secondDate)
                        {
                            dueDate = dueDate.Day.Equals(firstDate)
                                ? new DateTime(dueDate.Year, dueDate.Month, secondDate)
                                : new DateTime(dueDate.AddMonths(1).Year, dueDate.AddMonths(1).Month, firstDate);
                        }
                        else
                        {
                            dueDate = dueDate.Day.Equals(firstDate)
                                ? new DateTime(dueDate.AddMonths(1).Year, dueDate.AddMonths(1).Month, secondDate)
                                : new DateTime(dueDate.Year, dueDate.Month, firstDate);
                        }
                        

                    }
                    catch
                    {
                        firstDate = firstDate - 1;
                        secondDate = secondDate - 1;
                        goto repeat;
                    }
                    
                }
                else
                {
                    dueDate = GetNextDueDate(dueDate, loan.ProceedTypeID); 
                }

            }

            return schedule.OrderBy(o => o.DueDate).ToList();
        }

        private decimal GetScheduleAmount(decimal receivableAmount, int term, int proceedTypeID)
        {
            decimal amount = 0;
            decimal due = receivableAmount / term;
            amount = Math.Ceiling(due);

            return amount;
        }

        private void GenerateLoanSchedule(DatabaseContext db, Loan loan)
        {
            // check for existing schedule, exit if any.
            if (db.LoanPaymentSchedules.Where(w => w.DocumentNo == loan.DocumentNo).Any())
                return;

            // var proceedTypeID = (int)AdministrationHelper.ProceedType.Daily;
            var proceedTypeID = loan.ProceedTypeID;
            var term = loan.Term;
            var receivableAmount = loan.LoanAmount + (loan.LoanAmount * loan.LoanInterest / 100);

            var schedule = GetScheduleDates(loan);

            decimal totalAmount = 0;
            decimal amount = GetScheduleAmount(receivableAmount, schedule.Count(), proceedTypeID);

            for (var i = 0; i < schedule.Count(); i++)
            {
                
                if (i > 0) totalAmount = totalAmount + amount;

                if (i == (schedule.Count() - 1))
                {
                    schedule[i].ScheduledAmount = receivableAmount - totalAmount;
                    schedule[i].DueAmount = receivableAmount - totalAmount;
                }
                else
                {
                    schedule[i].ScheduledAmount = amount;
                    schedule[i].DueAmount = amount;
                }

                db.LoanPaymentSchedules.Add(schedule[i]);
            }


            loan.PaymentEndDate = schedule.Max(m => m.DueDate);
            db.Entry(loan).State = System.Data.EntityState.Modified;
            db.SaveChanges();


        }

        private DateTime GetNextDueDate(DateTime dueDate, int proceedTypeID)
        {
            switch (proceedTypeID)
            {
                case (int)AdministrationHelper.ProceedType.Daily:
                    {
                        dueDate = dueDate.AddDays(1);
                        break;
                    }
                case (int)AdministrationHelper.ProceedType.Weekly:
                    {
                        dueDate = dueDate.AddDays(7);
                        break;
                    }
                case (int)AdministrationHelper.ProceedType.SemiMonthly:
                    {
                        var date1 = 
                        dueDate = dueDate.AddDays(15);
                        break;
                    }
                case (int)AdministrationHelper.ProceedType.Monthly:
                    {
                        dueDate = dueDate.AddMonths(1);
                        break;
                    }
            }

            //if (dueDate.DayOfWeek == DayOfWeek.Sunday)
            //    dueDate = dueDate.AddDays(1);

            return dueDate;
        }

        private void DeleteLoanPaymentSchedule(DatabaseContext context, string documentNo)
        {
            try
            {
                var schedules = context.LoanPaymentSchedules.Where(w => w.DocumentNo == documentNo);

                foreach (var sched in schedules)
                {
                    context.LoanPaymentSchedules.Remove(sched);
                }

                context.SaveChanges();
            }
            catch (Exception ex)
            {
                View("Error", ex);
            }
        }

        private LoanPaymentSchedule CheckDueAmount(LoanPaymentSchedule loanPaymentSchedule)
        {
            loanPaymentSchedule.DueAmount = loanPaymentSchedule.ScheduledAmount + loanPaymentSchedule.OtherCharges;
            return loanPaymentSchedule;
        }

        private void PrintSchedule(DatabaseContext db, string documentNo)
        {
            Loan loan = db.Loans.Where(w => w.DocumentNo == documentNo).SingleOrDefault();
            List<LoanPaymentSchedule> schedule = db.LoanPaymentSchedules.Where(w => w.DocumentNo == documentNo).ToList();

            var data = GetSchedulePdf(db, loan, schedule);
            this.DownloadFile("PaymentSchedule_" + loan.DocumentNo + ".pdf", data);
        }

        private void SaveSchedule(DatabaseContext db, List<LoanPaymentSchedule> schedule)
        {
            foreach (var sched in schedule)
            {
                db.Entry(sched).State = System.Data.EntityState.Modified;
                db.SaveChanges();
            }
            
            
        }

        private byte[] GetSchedulePdf(DatabaseContext db, Loan loan, List<LoanPaymentSchedule> schedule)
        {
            var file = Server.MapPath("~/Documents/schedule.html");
            var html = System.IO.File.ReadAllText(file);

            string customerName = db.Borrowers.Where(w => w.ID == loan.BorrowerID).SingleOrDefault().BorrowerName;
            decimal receivable = loan.LoanAmount + (loan.LoanAmount * (loan.LoanInterest / 100));
            
            html = html.Replace("{0}", customerName);
            html = html.Replace("{1}", DateTime.Today.ToString("MM/dd/yyyy"));
            html = html.Replace("{2}", loan.LoanAmount.ToString("###,###,##0.00"));
            html = html.Replace("{3}", AdministrationHelper.GetLoanTypeName(loan.LoanTypeID));
            html = html.Replace("{4}", receivable.ToString("###,###,##0.00"));
            html = html.Replace("{5}", AdministrationHelper.GetPaymentTypeName(loan.PaymentTypeID));
            html = html.Replace("{6}", AdministrationHelper.GetProceedTypeName(loan.ProceedTypeID));
            html = html.Replace("{7}", loan.PaymentStartDate.GetValueOrDefault().ToString("MM/dd/yyyy") + " to " + loan.PaymentEndDate.GetValueOrDefault().ToString("MM/dd/yyyy"));
            html = html.Replace("{8}", schedule.Count.ToString());

            var schedString = string.Empty;
            for(var i = 0; i < schedule.Count; i++)
            {
                var itemString = "<tr><td>{10}</td><td>{11}</td><td>{12}</td><td>{13}</td></tr>";
                itemString = itemString.Replace("{10}", i.ToString());
                itemString = itemString.Replace("{11}", schedule[i].DueDate.ToString("MM/dd/yyyy"));
                itemString = itemString.Replace("{12}", schedule[i].ChequeDetails);
                itemString = itemString.Replace("{13}", schedule[i].DueAmount.ToString("###,###,##0.00"));

                schedString = schedString + itemString;
            }

            html = html.Replace("{9}", schedString);
            html = html.Replace("{10}", schedule.Sum(s => s.DueAmount).ToString("###,###,##0.00"));

            var logoFile = Server.MapPath(@"~/Documents/eqglogo2.png");
            byte[] logo = System.IO.File.ReadAllBytes(logoFile);

            byte[] data = ReportHelper.ExportToPDF(html, logo, PageSize.LETTER, marginTop: 40f, marginLeft: 80f, marginRight: 90f);

            return data;
        }
    }
}

